<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>Java单元测试Mock神器 | 奉孝的测开日记</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  
  
  
    <link rel="alternate" href="/blog/atom.xml" title="奉孝的测开日记" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/blog/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/blog/css/style.css">

  
    
<link rel="stylesheet" href="/blog/fancybox/jquery.fancybox.min.css">

  
  
    
<link rel="stylesheet" href="/blog/localshare/css/share.css">

  
  
  
<meta name="generator" content="Hexo 5.4.2"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/blog/" id="logo">奉孝的测开日记</a>
      </h1>
      
        <h2 id="subtitle-wrap">
          <a href="/blog/" id="subtitle">开启自我改变的原动力</a>
        </h2>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        
          <a class="main-nav-link" href="/blog/."><i class="fa fa-home"></i> 首页</a>
        
          <a class="main-nav-link" href="/blog/archives/"><i class="fa fa-archive"></i> 归档</a>
        
          <a class="main-nav-link" href="/blog/about/"><i class="fa fa-user"></i> 关于</a>
        
      </nav>
    </div>
    <div id="search-form">
      <div id="result-mask" class="hide"></div>
      <label><input id="search-key" type="text" autocomplete="off" placeholder="搜索"></label>
      <div id="result-wrap" class="hide">
        <div id="search-result"></div>
      </div>
      <div class="hide">
        <template id="search-tpl">
          <div class="item">
            <a href="/{path}" title="{title}">
              <div class="title">{title}</div>
              <div class="time">{date}</div>
              <div class="tags">{tags}</div>
            </a>
          </div>
        </template>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main"><article id="post-Java单元测试Mock神器" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="p-name article-title" itemprop="headline name">
      Java单元测试Mock神器
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-03-09T10:00:48.000Z" itemprop="datePublished">2023年03月09日</time>
</span>
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/blog/categories/Java/">Java</a>
  </div>

      
        <span class="article-views">
  <i class="fa fa-views"></i>
  <i id="busuanzi_container_page_pv">
      <i id="busuanzi_value_page_pv"></i>
  </i>
</span>

      
      
<a href="/blog/2023/03/09/Java%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95Mock%E7%A5%9E%E5%99%A8/#comments" class="article-comment-link">
  
    
    
    
    
    
  
  <i class="fa fa-commt"></i>
  留言
</a>


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="TestableMock简介"><a href="#TestableMock简介" class="headerlink" title="TestableMock简介"></a>TestableMock简介</h1><p>官方介绍”一款特立独行的轻量Mock工具，以<strong>简化Java单元测试</strong>为目标的综合辅助工具集”</p>
<h1 id="Maven项目中使用"><a href="#Maven项目中使用" class="headerlink" title="Maven项目中使用"></a>Maven项目中使用</h1><p>在<strong>dependencies</strong>列表添加TestableMock依赖  </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;com.alibaba.testable&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;testable-all&lt;/artifactId&gt;</span><br><span class="line">    &lt;version&gt;0.7.9&lt;/version&gt;</span><br><span class="line">    &lt;scope&gt;test&lt;/scope&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure>
<p>最后在<strong>build</strong>区域的<strong>plugins</strong>列表里添加<strong>maven-surefire-plugin</strong>插件  </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;plugin&gt;</span><br><span class="line">    &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;</span><br><span class="line">    &lt;configuration&gt;</span><br><span class="line">        &lt;argLine&gt;-javaagent:$&#123;settings.localRepository&#125;/com/alibaba/testable/testable-agent/0.7.9/testable-agent-0.7.9.jar&lt;/argLine&gt;</span><br><span class="line">    &lt;/configuration&gt;</span><br><span class="line">&lt;/plugin&gt;</span><br></pre></td></tr></table></figure>

<h1 id="TestableMock可以Mock什么"><a href="#TestableMock可以Mock什么" class="headerlink" title="TestableMock可以Mock什么"></a>TestableMock可以Mock什么</h1><h2 id="快速Mock任意调用"><a href="#快速Mock任意调用" class="headerlink" title="快速Mock任意调用"></a>快速Mock任意调用</h2><p>官方介绍”使被测类的任意方法调用快速替换为Mock方法，实现”指哪换哪”，解决传统Mock工具使用繁琐的问题”<br><strong>使用场景</strong><br>项目中有支付回调的场景，需要测试回调的方法逻辑是否正确。<br><strong>痛点</strong><br>方法中有调用其他jar包中的方法，而且该方法中还有很多逻辑，但这些逻辑并不是我们关注的<br><strong>解决</strong><br>待测方法  </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public String parseOrderNotifyResult(String xmlData) &#123;</span><br><span class="line">    try &#123;</span><br><span class="line">        WxPayOrderNotifyResult notifyResult = wxStudentMiniPayService.parseOrderNotifyResult(xmlData);</span><br><span class="line">    ...</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure>
<p>wxStudentMiniPayService.parseOrderNotifyResult(xmlData)<br>该方法为maven引入jar包中的方法，我们的目的就是Mock该方法，从而只关注<br>待测方法的逻辑<br>测试case,直接看代码  </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">@SpringBootTest</span><br><span class="line">public class PayServiceImplTest extends AbstractTestNGSpringContextTests &#123;</span><br><span class="line"></span><br><span class="line">    @Resource</span><br><span class="line">    private PayServiceImpl payService;</span><br><span class="line">    </span><br><span class="line">    public static class Mock&#123;</span><br><span class="line">        @MockInvoke(targetClass = WxPayService.class)</span><br><span class="line">        public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) &#123;</span><br><span class="line">            WxPayOrderNotifyResult wxPayOrderNotifyResult = new WxPayOrderNotifyResult();</span><br><span class="line">            wxPayOrderNotifyResult.setTransactionId(&quot;11111&quot;);</span><br><span class="line">            wxPayOrderNotifyResult.setResultCode(&quot;SUCCESS&quot;);</span><br><span class="line">            wxPayOrderNotifyResult.setOutTradeNo(&quot;74afe6ab214248b49898a832da79b742&quot;);</span><br><span class="line">            wxPayOrderNotifyResult.setDeviceInfo(&quot;KLASS&quot;);</span><br><span class="line">            return wxPayOrderNotifyResult;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Test(description = &quot;支付回调&quot;)</span><br><span class="line">    public void testParseOrderNotifyResult()&#123;</span><br><span class="line">        String xmlData = &quot;&quot;;</span><br><span class="line">        String res = payService.parseOrderNotifyResult(xmlData);</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<p>此时，执行测试case，待测方法中<code>WxPayOrderNotifyResult notifyResult = wxStudentMiniPayService.parseOrderNotifyResult(xmlData)</code><br>notifyResult返回的结果就是Mock容器中对应的数据。<br>上面代码，就是在测试类中添加了一个名称为Mock的静态内部类，拷贝原方法定义到Mock容器类，加@MockInvoke注解。这也是官方推荐的最简单的做法  </p>
<h2 id="访问被测类私有成员"><a href="#访问被测类私有成员" class="headerlink" title="访问被测类私有成员"></a>访问被测类私有成员</h2><p>TestableMock提供了PrivateAccessor工具类</p>
<h3 id="访问私有方法"><a href="#访问私有方法" class="headerlink" title="访问私有方法"></a>访问私有方法</h3><p>PrivateAccessor.invoke(任意对象, “私有方法名”, 调用参数…) ➜ 调用任意类的私有方法<br><strong>使用场景</strong><br>通过公有方法间接测私有方法比较繁琐，公有方法有复杂的入参，核心业务逻辑在私有方法中，我们更加关注私有方法的业务逻辑。<br><strong>痛点</strong><br>未使用TestableMock时，我是修改了方法的可见性，private变为public，这种方法比较繁琐<br><strong>解决</strong><br>待测方法  </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">private Student register(Map&lt;String, String&gt; paramMap) &#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如何借助PrivateAccessor测试该私有方法呢？  </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">@Test(description = &quot;团购入口注册&quot;)</span><br><span class="line">public void registerTest() &#123;</span><br><span class="line">    Map&lt;String, String&gt; paramMap = new HashMap&lt;String, String&gt;()&#123;</span><br><span class="line">        &#123;</span><br><span class="line">            put(&quot;areaCode&quot;, &quot;86&quot;);</span><br><span class="line">            put(&quot;mobile&quot;, &quot;19900000007&quot;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    Student student = PrivateAccessor.invoke(groupService, &quot;register&quot;, paramMap);</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>除了访问私有方法，还读取私有字段、修改私有字段、读取静态私有字段、修改静态私有字段、调用静态私有方法、调用私有构造方法，目前还未在单元测试</p>
<h3 id="修改私有字段"><a href="#修改私有字段" class="headerlink" title="修改私有字段"></a>修改私有字段</h3><p>PrivateAccessor.set(任意对象, “私有字段名”, 新的值)</p>
<p><strong>使用场景</strong></p>
<p>修改私有字段值，达到测试某种逻辑，比如下面待测方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ENV_DEV</span> <span class="operator">=</span> <span class="string">&quot;dev&quot;</span>;</span><br><span class="line"><span class="meta">@Value(&quot;$&#123;spring.profiles.active&#125;&quot;)</span></span><br><span class="line"><span class="keyword">private</span> String env;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Pair&lt;Integer, Integer&gt; <span class="title function_">sourceAndRealCommission</span><span class="params">(Byte direct, Integer product, <span class="type">boolean</span> isTest)</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">        Table&lt;Byte, Integer, Integer&gt; commissionMap = TYPE_PRODUCT_COMMISSION_MAP;</span><br><span class="line">        <span class="keyword">if</span> (isTest || Objects.equals(ENV_DEV, env)) &#123;</span><br><span class="line">            commissionMap = TEST_TYPE_PRODUCT_COMMISSION_MAP;</span><br><span class="line">        &#125;</span><br><span class="line">  			...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>正常测试环境，必然会走if逻辑中的代码，ENV_DEV为常量值，env在测试环境值为dev，想要if条件变为false，将代码中的常量ENV_DEV的值变为prd，或者将启动环境变为非测试环境，这种方法显然不灵活，但是PrivateAccessor就可以不改源码，轻松做到</p>
<p>测试方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test(description = &quot;获取购买不同产品，返现的金额。&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sourceAndRealCommissionTest</span><span class="params">()</span>&#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">isTest</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        PrivateAccessor.set(commissionService, <span class="string">&quot;env&quot;</span>, <span class="string">&quot;prd&quot;</span>);</span><br><span class="line">        ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>也可以更改ENV_DEV的值，但不建议，static final修饰的成员变量通常代表恒定常量值，不应该在单元测试中修改。确实要改，TestableMock也提供了解决方案，可以看官方文档。</p>
<h2 id="快速构造参数对象"><a href="#快速构造参数对象" class="headerlink" title="快速构造参数对象"></a>快速构造参数对象</h2><p><strong>使用场景</strong></p>
<p>被测方法需要有复杂的入参</p>
<p>被测方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">fetchCipher</span><span class="params">(HttpServletResponse response)</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们使用OmniConstructor快速构造入参，测试方法如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test(description = &quot;获取签名需要使用的key&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">fetchCipherTest</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="type">HttpServletResponse</span> <span class="variable">response</span> <span class="operator">=</span> OmniConstructor.newInstance(HttpServletResponse.class);</span><br><span class="line">       <span class="type">String</span> <span class="variable">res</span> <span class="operator">=</span> loginService.fetchCipher(response);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="辅助测试void方法"><a href="#辅助测试void方法" class="headerlink" title="辅助测试void方法"></a>辅助测试void方法</h2>
        
            <div id="toc-article">
                
  <div class="widget-wrap" id="toc-wrap">
    <h3 class="widget-title"><i class="fa fa-toc"></i> 文章目录</h3>
    <div class="widget">
      <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#TestableMock%E7%AE%80%E4%BB%8B"><span class="toc-text">TestableMock简介</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Maven%E9%A1%B9%E7%9B%AE%E4%B8%AD%E4%BD%BF%E7%94%A8"><span class="toc-text">Maven项目中使用</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#TestableMock%E5%8F%AF%E4%BB%A5Mock%E4%BB%80%E4%B9%88"><span class="toc-text">TestableMock可以Mock什么</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BF%AB%E9%80%9FMock%E4%BB%BB%E6%84%8F%E8%B0%83%E7%94%A8"><span class="toc-text">快速Mock任意调用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%AE%BF%E9%97%AE%E8%A2%AB%E6%B5%8B%E7%B1%BB%E7%A7%81%E6%9C%89%E6%88%90%E5%91%98"><span class="toc-text">访问被测类私有成员</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AE%BF%E9%97%AE%E7%A7%81%E6%9C%89%E6%96%B9%E6%B3%95"><span class="toc-text">访问私有方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BF%AE%E6%94%B9%E7%A7%81%E6%9C%89%E5%AD%97%E6%AE%B5"><span class="toc-text">修改私有字段</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BF%AB%E9%80%9F%E6%9E%84%E9%80%A0%E5%8F%82%E6%95%B0%E5%AF%B9%E8%B1%A1"><span class="toc-text">快速构造参数对象</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BE%85%E5%8A%A9%E6%B5%8B%E8%AF%95void%E6%96%B9%E6%B3%95"><span class="toc-text">辅助测试void方法</span></a></li></ol></li></ol>
    </div>
  </div>


            </div>
        
        
          <blockquote id="copyright">
              <p>原文链接: <a href="http://haohongbin.gitee.io/blog/2023/03/09/Java单元测试Mock神器/">http://haohongbin.gitee.io/blog/2023/03/09/Java单元测试Mock神器/</a></p>
              <p>版权声明: 转载请注明出处.</p>
          </blockquote>
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/blog/tags/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/" rel="tag">单元测试</a></li></ul>

          
    <div class="social-share">
      <span>分享到:</span>
    </div>



        </div>
      
      
        
<nav id="article-nav">
  
    <a href="/blog/2023/02/21/Pytest%E6%89%A7%E8%A1%8C%E7%94%A8%E4%BE%8B%E7%9A%84%E5%A4%9A%E7%A7%8D%E6%96%B9%E5%BC%8F/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">older</strong>
      <div class="article-nav-title">
        
          Pytest执行用例的多种方式
        
      </div>
    </a>
  
  
    <a href="/blog/2023/03/14/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%88%97%E8%A1%A8%E5%92%8C%E5%85%83%E7%BB%84/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">newer</strong>
      <div class="article-nav-title">
        
          (no title)
        
      </div>
    </a>
  
</nav>

      
      
        








      
    </footer>
  </div>
</article>
</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-posts"></i> 最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/blog/2023/03/14/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%88%97%E8%A1%A8%E5%92%8C%E5%85%83%E7%BB%84/">(no title)</a>
          </li>
        
          <li>
            <a href="/blog/2023/03/09/Java%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95Mock%E7%A5%9E%E5%99%A8/">Java单元测试Mock神器</a>
          </li>
        
          <li>
            <a href="/blog/2023/02/21/Pytest%E6%89%A7%E8%A1%8C%E7%94%A8%E4%BE%8B%E7%9A%84%E5%A4%9A%E7%A7%8D%E6%96%B9%E5%BC%8F/">Pytest执行用例的多种方式</a>
          </li>
        
          <li>
            <a href="/blog/2023/02/20/SQL%E6%9F%A5%E8%AF%A2%E5%9C%BA%E6%99%AF/">SQL查询场景</a>
          </li>
        
          <li>
            <a href="/blog/2023/01/18/MySQL%E5%91%BD%E4%BB%A4%E6%89%8B%E5%86%8C/">MySQL命令手册</a>
          </li>
        
      </ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-tag"></i> 标签云</h3>
    <div class="widget tagcloud">
      <a href="/blog/tags/MySQL/" style="font-size: 15px;">MySQL</a> <a href="/blog/tags/%E5%88%86%E4%BA%AB/" style="font-size: 15px;">分享</a> <a href="/blog/tags/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/" style="font-size: 10px;">单元测试</a> <a href="/blog/tags/%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/" style="font-size: 20px;">学习总结</a> <a href="/blog/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/" style="font-size: 10px;">自动化</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-classify"></i> 分类</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/blog/categories/Java/">Java</a><span class="category-list-count">5</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/Java%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF/">Java核心技术</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/hexo%E5%8D%9A%E5%AE%A2/">hexo博客</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/vim/">vim</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/%E5%90%8E%E7%AB%AF/">后端</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/%E6%B5%8B%E8%AF%95/">测试</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/%E6%B5%8B%E8%AF%95%E8%BF%9B%E9%98%B6/">测试进阶</a><span class="category-list-count">1</span></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-archive"></i> 归档</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2023/">2023年</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2022/">2022年</a><span class="archive-list-count">10</span></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-tag"></i> 标签</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/MySQL/" rel="tag">MySQL</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%88%86%E4%BA%AB/" rel="tag">分享</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/" rel="tag">单元测试</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/" rel="tag">学习总结</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/" rel="tag">自动化</a><span class="tag-list-count">1</span></li></ul>
    </div>
  </div>


  
    

  
</aside>
        
      </div>
      <a id="totop" href="#top"></a>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      <p>
        <a href="/blog/sitemap.xml">网站地图</a>
        <span> | </span><a href="/blog/atom.xml">订阅本站</a>
        <span> | </span><a href="/blog/about/">联系博主</a>
      </p>
      
        <p>
          <i class="fa fa-visitors"></i>
          <i id="busuanzi_container_site_uv"><i id="busuanzi_value_site_uv"></i></i>
          ，
          <i class="fa fa-views"></i>
          <i id="busuanzi_container_site_pv"><i id="busuanzi_value_site_pv"></i></i>
        </p>
      
      <p>
        <span>Copyright &copy; 2023 奉孝.</span>
        <span>Theme by <a href="https://github.com/chaooo/hexo-theme-BlueLake/" target="_blank">BlueLake.</a></span>
        <span>Powered by <a href="https://hexo.io/" target="_blank">Hexo.</a></span>
      </p>
    </div>
  </div>
</footer>

    </div>
  </div>
  
<script src="/blog/js/jquery-3.4.1.min.js"></script>


<script src="/blog/js/search.json.js"></script>


  
<script src="/blog/fancybox/jquery.fancybox.min.js"></script>




<script src="/blog/js/script.js"></script>






  
<script src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>




  
    
<script src="/blog/localshare/js/social-share.js"></script>

    
<script src="/blog/localshare/js/qrcode.js"></script>

  
  



  

  

  

  

  

  

  

  
  





</body>
</html>